package com.jinly.ryder.deploy.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.jinly.ryder.build.service.model.BuildArtifact;
import com.jinly.ryder.common.error.SystemErrorType;
import com.jinly.ryder.deploy.service.DeployService;
import com.jinly.ryder.deploy.service.model.DeployRecord;
import com.jinly.ryder.deploy.service.model.DeployType;
import com.jinly.ryder.deploy.task.DeployTask;
import com.jinly.ryder.deploy.task.DeployTaskParam;
import com.jinly.ryder.flow.service.FlowApplyQueryService;
import com.jinly.ryder.flow.service.model.FlowApply;
import com.jinly.ryder.flow.service.model.FlowAudit;
import com.jinly.ryder.manage.service.AppInstanceService;
import com.jinly.ryder.manage.service.DeployGroupService;
import com.jinly.ryder.manage.service.model.AppInstance;
import com.jinly.ryder.manage.service.model.DeployGroup;
import com.jinly.ryder.manage.service.model.DeployStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class DeployServiceImpl implements DeployService {

    private final DeployGroupService deployGroupService;
    private final AppInstanceService appInstanceService;
    private final FlowApplyQueryService flowApplyQueryService;
    private final AsyncTaskExecutor deployTaskExecutor;

    private String deployTempDir = System.getProperty("java.io.tmpdir") + "deploy";

    @Override
    public void deployApply(FlowApply flowApply, FlowAudit flowAudit) {
        JSONObject formData = flowApply.getApplyFormData();
        DeployGroup applyDeployGroup = formData.getObject("deployGroup", DeployGroup.class);
        DeployGroup deployGroup = deployGroupService.get(applyDeployGroup.getId());
        switch (flowApply.getApplyStatus()) {
            case SUBMIT:
                Assert.isTrue(deployGroup != null, "分组不能为空");
                Assert.isTrue(formData.containsKey("artifact"), "发布包不能为空");
                if (deployGroup.getDeployStatus() == DeployStatus.WAITING) {
                    SystemErrorType.BAD_REQUEST.throwError("其他发布申请正在审核中");
                }
                deployGroupService.changeDeployStatus(deployGroup.getId(), DeployStatus.WAITING, flowApply.getId());
                break;
            case CANCEL:
            case REJECT:
                if (deployGroup.getDeployStatus() == DeployStatus.WAITING) {
                    deployGroupService.changeDeployStatus(deployGroup.getId(), DeployStatus.NONE, flowApply.getId());
                }
                break;
            case APPROVE:
                if (deployGroup.getDeployStatus() == DeployStatus.WAITING) {
                    deployGroupService.changeDeployStatus(deployGroup.getId(), DeployStatus.DEPLOYING, flowApply.getId());
                }
                break;
        }
    }

    /**
     * 根据分组部署
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deployByGroup(DeployGroup deployGroup, Long operator) {
        if (deployGroup == null || deployGroup.getDeployStatus() != DeployStatus.DEPLOYING) return;
        FlowApply flowApply = flowApplyQueryService.applyDetail(deployGroup.getApplyId());

        List<AppInstance> appInstanceList = appInstanceService.list(deployGroup.getAppName(), deployGroup.getName());
        // 创建部署任务
        deployInstances(deployGroup, appInstanceList, flowApply, DeployType.DEPLOY, operator);
    }


    /**
     * 根据实例部署
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deployByInstance(AppInstance appInstance, Long operator) {
        DeployGroup deployGroup = deployGroupService.get(appInstance.getAppName(), appInstance.getDeployGroupName());
        if (deployGroup == null || deployGroup.getDeployStatus() != DeployStatus.DEPLOYING) return;
        // 创建部署任务
        FlowApply flowApply = flowApplyQueryService.applyDetail(deployGroup.getApplyId());
        deployInstances(deployGroup, List.of(appInstance), flowApply, DeployType.DEPLOY, operator);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void restartByInstance(AppInstance appInstance, Long operator) {
        DeployGroup deployGroup = deployGroupService.get(appInstance.getAppName(), appInstance.getDeployGroupName());
        if (deployGroup == null) return;
        // 创建部署任务
        deployInstances(deployGroup, List.of(appInstance), null, DeployType.RESTART, operator);
    }

    private void deployInstances(DeployGroup deployGroup, List<AppInstance> appInstanceList, FlowApply flowApply, DeployType deployType, Long operator) {
        if (appInstanceList.stream().anyMatch(ins -> ins.getDeployStatus() == DeployStatus.DEPLOYING)) {
            throw new RuntimeException("实例正在发布中");
        }
        String deployRootDir = deployTempDir + "/" + deployGroup.getAppName() + "/" + deployGroup.getName();
        DeployTaskParam param = new DeployTaskParam();
        param.setOperator(operator);
        param.setDeployType(deployType);
        if (flowApply != null) {
            param.setFlowApplyId(flowApply.getId());
            param.setArtifact(flowApply.getApplyFormData().getObject("artifact", BuildArtifact.class));
        }
        param.setInstanceList(appInstanceList);
        param.setDeployGroup(deployGroup);
        param.setDeployRootDir(deployRootDir);
        deployTaskExecutor.submit(new DeployTask(param));
    }


    @Override
    public void updateInstanceDeployStatus(Integer instanceId, DeployStatus deployStatus) {
        appInstanceService.updateDeployStatus(instanceId, deployStatus);
    }

    @Override
    public void updateInstanceDeployStatus(DeployRecord deployRecord) {
        appInstanceService.updateDeployStatus(deployRecord.getInstanceId(), deployRecord.getSuccess() ? DeployStatus.DEPLOY_SUCCESS : DeployStatus.DEPLOY_FAIL);
    }
}
